using System.Data;
using Dapper;

namespace Dapper.Bulk;

public class BulkOperations : IBulkOperations
{
    private readonly IDbConnection _connection;

    public BulkOperations(IDbConnection connection)
    {
        _connection = connection;
    }

    public async Task<int> BulkInsertAsync<T>(IEnumerable<T> entities) where T : class
    {
        var mapping = BulkManager.GetMapping<T>();
        var entitiesList = entities.ToList();
        var totalAffected = 0;
        
        // 分批处理
        for (int i = 0; i < entitiesList.Count; i += mapping.BulkOptions.BatchSize)
        {
            var batch = entitiesList.Skip(i).Take(mapping.BulkOptions.BatchSize).ToList();
            
            // 生成批量插入 SQL
            var sql = SqlGenerator.GenerateBatchInsertSql(mapping, batch.Count);
            var parameters = SqlGenerator.CreateBatchParameters(mapping, batch);
            
            var affected = await _connection.ExecuteAsync(sql, parameters, 
                transaction: mapping.BulkOptions.Transaction, 
                commandTimeout: mapping.BulkOptions.Timeout);
            totalAffected += affected;
        }
        
        return totalAffected;
    }

    public async Task<int> BulkUpdateAsync<T>(IEnumerable<T> entities) where T : class
    {
        var mapping = BulkManager.GetMapping<T>();
        var entitiesList = entities.ToList();
        var totalAffected = 0;
        
        // 分批处理
        for (int i = 0; i < entitiesList.Count; i += mapping.BulkOptions.BatchSize)
        {
            var batch = entitiesList.Skip(i).Take(mapping.BulkOptions.BatchSize).ToList();
            
            // 对于更新，我们使用多语句执行
            var sql = SqlGenerator.GenerateBatchUpdateSql(mapping, batch.Count);
            var parameters = SqlGenerator.CreateBatchParameters(mapping, batch, includeIdentity: true);
            
            var affected = await _connection.ExecuteAsync(sql, parameters, 
                transaction: mapping.BulkOptions.Transaction, 
                commandTimeout: mapping.BulkOptions.Timeout);
            totalAffected += affected;
        }
        
        return totalAffected;
    }

    public async Task<int> BulkDeleteAsync<T>(IEnumerable<T> entities) where T : class
    {
        var mapping = BulkManager.GetMapping<T>();
        if (mapping.IdentityProperty == null)
            throw new InvalidOperationException($"Identity property must be configured for delete operation on entity {typeof(T).Name}");

        var entitiesList = entities.ToList();
        var totalAffected = 0;
        
        // 分批处理
        for (int i = 0; i < entitiesList.Count; i += mapping.BulkOptions.BatchSize)
        {
            var batch = entitiesList.Skip(i).Take(mapping.BulkOptions.BatchSize).ToList();
            
            // 使用 IN 子句进行批量删除
            var ids = batch.Select(e => mapping.IdentityProperty.GetValue(e)).Where(id => id != null).ToList();
            if (ids.Any())
            {
                var sql = SqlGenerator.GenerateBatchDeleteSql(mapping, ids.Count);
                var parameters = new Dictionary<string, object>();
                for (int j = 0; j < ids.Count; j++)
                {
                    parameters[$"id{j}"] = ids[j]!;
                }
                
                var affected = await _connection.ExecuteAsync(sql, parameters, 
                    transaction: mapping.BulkOptions.Transaction, 
                    commandTimeout: mapping.BulkOptions.Timeout);
                totalAffected += affected;
            }
        }
        
        return totalAffected;
    }

    public async Task<int> BulkMergeAsync<T>(IEnumerable<T> entities) where T : class
    {
        var mapping = BulkManager.GetMapping<T>();
        if (mapping.IdentityProperty == null)
            throw new InvalidOperationException($"Identity property must be configured for merge operation on entity {typeof(T).Name}");

        var entitiesList = entities.ToList();
        var totalAffected = 0;

        // 分批处理
        for (int i = 0; i < entitiesList.Count; i += mapping.BulkOptions.BatchSize)
        {
            var batch = entitiesList.Skip(i).Take(mapping.BulkOptions.BatchSize).ToList();
            
            // 检查哪些记录已存在
            var existsSql = SqlGenerator.GenerateExistsSql(mapping);
            var existingIds = await _connection.QueryAsync<object>(existsSql, batch, 
                transaction: mapping.BulkOptions.Transaction, 
                commandTimeout: mapping.BulkOptions.Timeout);
            var existingIdSet = new HashSet<object>(existingIds);

            // 分离插入和更新的记录
            var toInsert = new List<T>();
            var toUpdate = new List<T>();

            foreach (var entity in batch)
            {
                var idValue = mapping.IdentityProperty.GetValue(entity);
                if (idValue != null && existingIdSet.Contains(idValue))
                {
                    toUpdate.Add(entity);
                }
                else
                {
                    toInsert.Add(entity);
                }
            }

            // 执行插入
            if (toInsert.Any())
            {
                totalAffected += await BulkInsertAsync(toInsert);
            }

            // 执行更新
            if (toUpdate.Any())
            {
                totalAffected += await BulkUpdateAsync(toUpdate);
            }
        }

        return totalAffected;
    }

    public async Task<int> BulkSynchronizeAsync<T>(IEnumerable<T> entities) where T : class
    {
        var mapping = BulkManager.GetMapping<T>();
        if (mapping.IdentityProperty == null)
            throw new InvalidOperationException($"Identity property must be configured for synchronize operation on entity {typeof(T).Name}");

        var entitiesList = entities.ToList();
        
        // 获取所有现有记录
        var selectAllSql = SqlGenerator.GenerateSelectSql(mapping);
        var existingEntities = await _connection.QueryAsync<T>(selectAllSql, 
            transaction: mapping.BulkOptions.Transaction, 
            commandTimeout: mapping.BulkOptions.Timeout);
        var existingIds = existingEntities.Select(e => mapping.IdentityProperty.GetValue(e)).Where(id => id != null).ToHashSet();
        
        // 获取目标状态的ID
        var targetIds = entitiesList.Select(e => mapping.IdentityProperty.GetValue(e)).Where(id => id != null).ToHashSet();
        
        var totalAffected = 0;

        // 删除不在目标状态中的记录
        var idsToDelete = existingIds.Except(targetIds).ToList();
        if (idsToDelete.Any())
        {
            var deleteSql = SqlGenerator.GenerateDeleteSql(mapping);
            var deleteEntities = idsToDelete.Select(id => 
            {
                var entity = Activator.CreateInstance<T>();
                mapping.IdentityProperty.SetValue(entity, id);
                return entity;
            });
            totalAffected += await _connection.ExecuteAsync(deleteSql, deleteEntities, 
                transaction: mapping.BulkOptions.Transaction, 
                commandTimeout: mapping.BulkOptions.Timeout);
        }

        // 执行合并操作（插入新记录，更新现有记录）
        totalAffected += await BulkMergeAsync(entitiesList);

        return totalAffected;
    }
}